#ifndef DATASTRUCT_H
#define DATASTRUCT_H

class nefFont
{
  private:
  public:
    TTF_Font *font;
    void Open(const char *file, int size)
    {
        font = TTF_OpenFont(file, size);
        if (font == NULL)
        {
            fprintf(stderr, "font open failure %s\n", SDL_GetError());
            clean_up();
            exit(-1);
        }
    }
    void Set(int style)
    {
        TTF_SetFontStyle(font, style);
    }
    void Free()
    {
        TTF_CloseFont(font);
    }
};

class nefTexture
{
  private:
    GLfloat texcoord[4];
    GLfloat *light_mat_ambient;
    GLfloat *light_mat_diffuse;
    GLfloat *light_mat_specular;
    GLfloat *light_mat_emission;
    GLfloat light_mat_shininess;
    GLfloat *light_mat_ambient_default;
    GLfloat *light_mat_diffuse_default;
    GLfloat *light_mat_specular_default;
    GLfloat *light_mat_emission_default;
    GLfloat light_mat_shininess_default;
    GLuint texture_default;

  public:
    GLfloat texMinX, texMinY;
    GLfloat texMaxX, texMaxY;
    GLuint texture;
    int width, height;
    nefTexture()
    {
        light_mat_ambient = new GLfloat[4];
        light_mat_diffuse = new GLfloat[4];
        light_mat_specular = new GLfloat[4];
        light_mat_emission = new GLfloat[4];
        light_mat_ambient_default = new GLfloat[4];
        light_mat_diffuse_default = new GLfloat[4];
        light_mat_specular_default = new GLfloat[4];
        light_mat_emission_default = new GLfloat[4];
        light_mat_shininess = 0;
        light_mat_shininess_default = 50;
        memset(light_mat_ambient, 0, 4);
        memset(light_mat_diffuse, 0, 4);
        memset(light_mat_specular, 0, 4);
        memset(light_mat_emission, 0, 4);
        memset(light_mat_ambient_default, 0, 4);
        memset(light_mat_diffuse_default, 1, 4);
        memset(light_mat_specular_default, 1, 4);
        memset(light_mat_emission_default, 1, 4);
        light_mat_ambient_default[3] = 1;
    }
    void RenderText(TTF_Font *font, const char *text, SDL_Color color)
    {
        color.r *= 255;
        color.g *= 255;
        color.b *= 255;
        SDL_Surface *textSurface = TTF_RenderText_Solid(font, text, color);
        texture = SDL_GL_LoadTexture(textSurface, texcoord);
        texMinX = texcoord[0];
        texMinY = texcoord[1];
        texMaxX = texcoord[2];
        texMaxY = texcoord[3];
        width = textSurface->w;
        height = textSurface->h;
    }
    int LoadImage(const char *file)
    {
        char *filename = new char[50];
        strcpy(filename, file);
        SDL_Surface *imageSurface = load_image(filename);
        texMinX = 0;
        texMinY = 0;
        texMaxX = 1;
        texMaxY = 1;
        width = imageSurface->w;
        height = imageSurface->h;
        texture = SOIL_load_OGL_texture(
            file,
            SOIL_LOAD_AUTO,
            SOIL_CREATE_NEW_ID,
            SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
        if (0 == texture)
        {
            printf("SOIL loading error: '%s'\n", SOIL_last_result());
            return -1;
        }
        delete filename;
        SDL_FreeSurface(imageSurface);

        texture_default = SOIL_load_OGL_texture(
            "./res/pic/default.png",
            SOIL_LOAD_AUTO,
            SOIL_CREATE_NEW_ID,
            SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
        if (0 == texture_default)
        {
            printf("SOIL loading error(default): '%s'\n", SOIL_last_result());
            return -1;
        }

        return 0;
    }
    void setAmbient(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
    {
        light_mat_ambient[0] = r;
        light_mat_ambient[1] = g;
        light_mat_ambient[2] = b;
        light_mat_ambient[3] = a;
    }
    void setDiffuse(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
    {
        light_mat_diffuse[0] = r;
        light_mat_diffuse[1] = g;
        light_mat_diffuse[2] = b;
        light_mat_diffuse[3] = a;
    }
    void setSpecular(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
    {
        light_mat_specular[0] = r;
        light_mat_specular[1] = g;
        light_mat_specular[2] = b;
        light_mat_specular[3] = a;
    }
    void setEmission(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
    {
        light_mat_emission[0] = r;
        light_mat_emission[1] = g;
        light_mat_emission[2] = b;
        light_mat_emission[3] = a;
    }
    void setShininess(GLfloat shininess)
    {
        light_mat_shininess = shininess;
    }
    void Blind()
    {
        glMaterialfv(GL_FRONT, GL_AMBIENT, light_mat_ambient);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, light_mat_diffuse);
        glMaterialfv(GL_FRONT, GL_SPECULAR, light_mat_specular);
        glMaterialfv(GL_FRONT, GL_EMISSION, light_mat_emission);
        glMaterialf(GL_FRONT, GL_SHININESS, light_mat_shininess);
        glBindTexture(GL_TEXTURE_2D, texture);
    }
    void unBlind()
    {
        glMaterialfv(GL_FRONT, GL_AMBIENT, light_mat_ambient_default);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, light_mat_diffuse_default);
        glMaterialfv(GL_FRONT, GL_SPECULAR, light_mat_specular_default);
        glMaterialfv(GL_FRONT, GL_EMISSION, light_mat_emission_default);
        glMaterialf(GL_FRONT, GL_SHININESS, light_mat_shininess_default);
        glBindTexture(GL_TEXTURE_2D, texture_default);
    }
    void Free()
    {
        glDeleteTextures(1, &texture_default);
        glDeleteTextures(1, &texture);
        delete[] light_mat_ambient;
        delete[] light_mat_diffuse;
        delete[] light_mat_specular;
        delete[] light_mat_emission;
    }
};

class nefLight
{
  private:
    GLfloat *light_position;
    GLfloat *light_ambient;
    GLfloat *light_diffuse;
    GLfloat *light_specular;
    GLfloat *light_position_off;
    GLfloat *light_ambient_off;
    GLfloat *light_diffuse_off;
    GLfloat *light_specular_off;
    GLenum ID;

  public:
    nefLight()
    {
        light_position = new GLfloat[4];
        light_ambient = new GLfloat[4];
        light_diffuse = new GLfloat[4];
        light_specular = new GLfloat[4];
        light_position_off = new GLfloat[4];
        light_ambient_off = new GLfloat[4];
        light_diffuse_off = new GLfloat[4];
        light_specular_off = new GLfloat[4];
        memset(light_position, 0, 4);
        memset(light_ambient, 0, 4);
        memset(light_diffuse, 0, 4);
        memset(light_specular, 0, 4);
        memset(light_position_off, 0, 4);
        memset(light_ambient_off, 0, 4);
        memset(light_diffuse_off, 0, 4);
        memset(light_specular_off, 0, 4);
    }
    ~nefLight()
    {
        delete[] light_position;
        delete[] light_ambient;
        delete[] light_diffuse;
        delete[] light_specular;
        delete[] light_position_off;
        delete[] light_ambient_off;
        delete[] light_diffuse_off;
        delete[] light_specular_off;
    }
    void setID(GLenum LID)
    {
        ID = LID;
        glEnable(ID);
    }
    void setPosition(GLfloat x, GLfloat y, GLfloat z, GLfloat w)
    {
        light_position[0] = x;
        light_position[1] = y;
        light_position[2] = z;
        light_position[3] = w;
    }
    void setAmbient(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
    {
        light_ambient[0] = r;
        light_ambient[1] = g;
        light_ambient[2] = b;
        light_ambient[3] = a;
    }
    void setDiffuse(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
    {
        light_diffuse[0] = r;
        light_diffuse[1] = g;
        light_diffuse[2] = b;
        light_diffuse[3] = a;
    }
    void setSpecular(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
    {
        light_specular[0] = r;
        light_specular[1] = g;
        light_specular[2] = b;
        light_specular[3] = a;
    }
    void turnOn()
    {
        glLightfv(ID, GL_POSITION, light_position);
        glLightfv(ID, GL_AMBIENT, light_ambient);
        glLightfv(ID, GL_DIFFUSE, light_diffuse);
        glLightfv(ID, GL_SPECULAR, light_specular);
    }
    void turnOff()
    {
        glLightfv(ID, GL_POSITION, light_position_off);
        glLightfv(ID, GL_AMBIENT, light_ambient_off);
        glLightfv(ID, GL_DIFFUSE, light_diffuse_off);
        glLightfv(ID, GL_SPECULAR, light_specular_off);
    }
};

class nefBrush
{
  private:
  public:
    void setColor(GLfloat r, GLfloat g, GLfloat b)
    {
        glColor3f(r, g, b);
    }
    void beginPoints()
    {
        glBegin(GL_POINT);
    }
    void beginLines()
    {
        glBegin(GL_LINE);
    }
    void beginPolygon()
    {
        glBegin(GL_POLYGON);
    }
    void addNormal(GLfloat nor_x, GLfloat nor_y, GLfloat nor_z)
    {
        glNormal3f(nor_x, nor_y, nor_z);
    }
    void addPoint(GLfloat pos_x, GLfloat pos_y, GLfloat pos_z,
                  GLfloat u, GLfloat v)
    {
        glTexCoord2f(u, v);
        glVertex3f(pos_x, pos_y, pos_z);
    }
    void end()
    {
        glEnd();
    }
    void HUD_drawText(nefTexture texture, int x, int y)
    {
        glColor3f(1.0, 1.0, 1.0);
        glBindTexture(GL_TEXTURE_2D, texture.texture);
        if (!DOUBLE_EYES)
        {
            glBegin(GL_TRIANGLE_STRIP);
            glTexCoord2f(texture.texMinX, texture.texMinY);
            glVertex2i(x, y);
            glTexCoord2f(texture.texMaxX, texture.texMinY);
            glVertex2i(x + texture.width, y);
            glTexCoord2f(texture.texMinX, texture.texMaxY);
            glVertex2i(x, y + texture.height);
            glTexCoord2f(texture.texMaxX, texture.texMaxY);
            glVertex2i(x + texture.width, y + texture.height);
            glEnd();
        }
        else
        {
            glViewport(0, 0, screen->w / 2, screen->h);
            glBegin(GL_TRIANGLE_STRIP);
            glTexCoord2f(texture.texMinX, texture.texMinY);
            glVertex2i(x, y);
            glTexCoord2f(texture.texMaxX, texture.texMinY);
            glVertex2i(x + texture.width, y);
            glTexCoord2f(texture.texMinX, texture.texMaxY);
            glVertex2i(x, y + texture.height);
            glTexCoord2f(texture.texMaxX, texture.texMaxY);
            glVertex2i(x + texture.width, y + texture.height);
            glEnd();

            glViewport(screen->w / 2, 0, screen->w / 2, screen->h);
            glBegin(GL_TRIANGLE_STRIP);
            glTexCoord2f(texture.texMinX, texture.texMinY);
            glVertex2i(x, y);
            glTexCoord2f(texture.texMaxX, texture.texMinY);
            glVertex2i(x + texture.width, y);
            glTexCoord2f(texture.texMinX, texture.texMaxY);
            glVertex2i(x, y + texture.height);
            glTexCoord2f(texture.texMaxX, texture.texMaxY);
            glVertex2i(x + texture.width, y + texture.height);
            glEnd();
        }
    }
    void HUD_drawImage(nefTexture texture, int x, int y, int width, int height)
    {
        glColor3f(1.0, 1.0, 1.0);
        glBindTexture(GL_TEXTURE_2D, texture.texture);
        if (!DOUBLE_EYES)
        {
            glBegin(GL_TRIANGLE_STRIP);
            glTexCoord2f(texture.texMinX, texture.texMaxY);
            glVertex2i(x, y);
            glTexCoord2f(texture.texMaxX, texture.texMaxY);
            glVertex2i(x + width, y);
            glTexCoord2f(texture.texMinX, texture.texMinY);
            glVertex2i(x, y + height);
            glTexCoord2f(texture.texMaxX, texture.texMinY);
            glVertex2i(x + width, y + height);
            glEnd();
        }
        else
        {
            glViewport(0, 0, screen->w / 2, screen->h);
            glBegin(GL_TRIANGLE_STRIP);
            glTexCoord2f(texture.texMinX, texture.texMaxY);
            glVertex2i(x, y);
            glTexCoord2f(texture.texMaxX, texture.texMaxY);
            glVertex2i(x + width, y);
            glTexCoord2f(texture.texMinX, texture.texMinY);
            glVertex2i(x, y + height);
            glTexCoord2f(texture.texMaxX, texture.texMinY);
            glVertex2i(x + width, y + height);
            glEnd();

            glViewport(screen->w / 2, 0, screen->w / 2, screen->h);
            glBegin(GL_TRIANGLE_STRIP);
            glTexCoord2f(texture.texMinX, texture.texMaxY);
            glVertex2i(x, y);
            glTexCoord2f(texture.texMaxX, texture.texMaxY);
            glVertex2i(x + width, y);
            glTexCoord2f(texture.texMinX, texture.texMinY);
            glVertex2i(x, y + height);
            glTexCoord2f(texture.texMaxX, texture.texMinY);
            glVertex2i(x + width, y + height);
            glEnd();
        }
    }
};

class nefAudio
{
  private:
    Mix_Music *audio;

  public:
    nefAudio()
    {
        Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
    }
    void Open(const char *file)
    {
        audio = Mix_LoadMUS(file);
    }
    void setVolume(int Vol)
    {
        Mix_VolumeMusic(Vol);
    }
    void Play()
    {
        Mix_PlayMusic(audio, 1);
    }
    void Pause()
    {
        Mix_PauseMusic();
    }
    void Resume()
    {
        Mix_ResumeMusic();
    }
    void Rewind()
    {
        Mix_RewindMusic();
    }
    void Free()
    {
        Mix_FreeMusic(audio);
    }
};

struct Posture
{
    double pos[3];
    double dir[3]; //左右 上下 逆顺
    double pre;
};

#endif